/*
 * wm8988.c -- WM8988 ALSA SoC audio driver
 *
 * Copyright 2009 Wolfson Microelectronics plc
 * Copyright 2005 Openedhand Ltd.
 *
 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
#include <sound/initval.h>

#include "cjc8988.h"

//*************************************************************************
// LINE Input 1 to ADC mode, slave mode, I2S format output, MCLK=12.288MHz, BCLK =3.072MHz, Fs=48KHz;
//
//static struct CJC8988_reg    LINPUT1_TO_ADC[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x0A},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x00},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x04},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x00},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x00},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_L,        0x52},   // Left DAC to left mixer disable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_L,       0x50},   // Right DAC to right mixer disable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x00},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x7c},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_L,            0x00},   // Power management2 and DAC left power down; R/W;
//};
//  
//#define CJC8988_LINPUT1_TO_ADC_REG_NUM    ARRAY_SIZE(LINPUT1_TO_ADC)
//
////*************************************************************************
//// DAC to LOUT1 mode, slave mode, I2S format input, MCLK=12.288MHz, BCLK =?MHz, Fs=48KHz;
////
//static struct CJC8988_reg    DAC_TO_LOUT1_TEST[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x30},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x30},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x0D},   // Digital Audio interface format; R/W
//    //{CJC8988_R7_AUDIO_INTERFACE,         0x01},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x00},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x04},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x00},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x00},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_H,        0x52},   // Left DAC to left mixer enable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_H,       0x50},   // Right DAC to right mixer enable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x08},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x40},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0xf8},   // Power management2 and DAC left power up; R/W;
//};
//  
//#define CJC8988_DAC_TO_LOUT1_REG_NUM_TEST    ARRAY_SIZE(DAC_TO_LOUT1_TEST)
//
////*************************************************************************
//// DAC to LOUT1 mode, slave mode, I2S format input, MCLK=12.288MHz, BCLK =3.072MHz, Fs=48KHz;
////
//static struct CJC8988_reg    DAC_TO_LOUT1[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x0A},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x00},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x00},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x00},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x00},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_H,        0x52},   // Left DAC to left mixer enable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_H,       0x50},   // Right DAC to right mixer enable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x08},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x40},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0xf8},   // Power management2 and DAC left power up; R/W;
//};
//  
//#define CJC8988_DAC_TO_LOUT1_REG_NUM    ARRAY_SIZE(DAC_TO_LOUT1)
//
////*************************************************************************
//// LINE Input 1 to LOUT 1 mode, slave mode, I2S format, MCLK=12.288MHz, BCLK =3.072MHz, Fs=48KHz;
////
//static struct CJC8988_reg    LINPUT1_TO_LOUT1[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x0A},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x00},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x00},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x00},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x00},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_L,        0xA0},   // Left DAC to left mixer disable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x50},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_L,       0xA0},   // Right DAC to right mixer disable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x00},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x40},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0x78},   // Power management2 and DAC left power up; R/W;
//};
//  
//#define CJC8988_LINPUT1_TO_LOUT1_REG_NUM    ARRAY_SIZE(LINPUT1_TO_LOUT1)
//
////*************************************************************************
//// LINE Input 2 to ADC and DAC to LOUT 2 mode, slave mode, I2S format, MCLK=12MHz, BCLK = 12MHz, Fs=8KHz;
////
//static struct CJC8988_reg    LINPUT2_ADC_TO_DAC_LOUT2_8[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x02},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x0d},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x00},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x40},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x40},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_H,        0x52},   // Left DAC to left mixer disable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_H,       0x50},   // Right DAC to right mixer disable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x08},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x7C},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0xf8},   // Power management2 and DAC left power up; R/W;
//};
//  
//#define CJC8988_LINPUT2_ADC_TO_DAC_LOUT2_8_REG_NUM    ARRAY_SIZE(LINPUT2_ADC_TO_DAC_LOUT2_8)
//
////*************************************************************************
//// LINE Input 2 to ADC and DAC to LOUT 2 mode, slave mode, I2S format, MCLK=12MHz, BCLK = 12MHz, Fs=32KHz;
////
//static struct CJC8988_reg    LINPUT2_ADC_TO_DAC_LOUT2_32[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x02},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x19},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x00},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x40},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x40},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_H,        0x52},   // Left DAC to left mixer disable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_H,       0x50},   // Right DAC to right mixer disable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x08},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x7C},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0xf8},   // Power management2 and DAC left power up; R/W;
//};
//  
//#define CJC8988_LINPUT2_ADC_TO_DAC_LOUT2_32_REG_NUM    ARRAY_SIZE(LINPUT2_ADC_TO_DAC_LOUT2_32)
//
////*************************************************************************
//// LINE Input 2 to ADC and DAC to LOUT 2 mode, slave mode, I2S format, MCLK=12MHz, BCLK = 12MHz, Fs=44.1KHz;
////
//static struct CJC8988_reg    LINPUT2_ADC_TO_DAC_LOUT2_44[] = {
//    {CJC8988_R0_LEFT_INPUT_VOLUME,       0x17},   // Audio input left channel volume; R/W
//    {CJC8988_R1_RIGHT_INPUT_VOLUME,      0x17},   // Audio input right channel volume; R/W
//    {CJC8988_R2_LOUT1_VOLUME,            0x79},   // Audio output letf channel1 volume; R/W
//    {CJC8988_R3_ROUT1_VOLUME,            0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R5_ADC_DAC_CONTROL,         0x00},   // ADC and DAC CONTROL; R/W
//    {CJC8988_R7_AUDIO_INTERFACE,         0x02},   // Digital Audio interface format; R/W
//    {CJC8988_R8_SAMPLE_RATE,             0x23},   // Clock and Sample rate control; R/W
//    {CJC8988_R10_LEFT_DAC_VOLUME,        0xff},   // Left channel DAC digital volume; R/W
//    {CJC8988_R11_RIGHT_DAC_VOLUME,       0xff},   // Right channel DAC digital volume; R/W
//
//    {CJC8988_R12_BASS_CONTROL,           0x0f},   // BASS control; R/W
//    {CJC8988_R13_TREBLE_CONTROL,         0x0f},   // Treble control; R/W
//    {CJC8988_R16_3D_CONTROL,             0x00},   // 3D control; R/W
//       
//    {CJC8988_R21_LEFT_ADC_VOLUME,        0xc3},   // Left ADC digital volume; R/W
//    {CJC8988_R22_RIGHT_ADC_VOLUME,       0xc3},   // Right ADC digital volume; R/W
//       
//    {CJC8988_R23_ADDITIONAL_CONTROL1,    0xc0},   // Additional control 1; R/W
//    {CJC8988_R24_ADDITIONAL_CONTROL2,    0x00},   // Additional control 2; R/W
//    {CJC8988_R27_ADDITIONAL_CONTROL3,    0x00},   // Additional control 3; R/W
//
//    {CJC8988_R31_ADC_INPUT_MODE,         0x00},   // ADC input mode; R/W
//    
//    {CJC8988_R32_ADCL_SIGNAL_PATH,       0x40},   // Left ADC signal path control; R/W
//    {CJC8988_R33_ADCR_SIGNAL_PATH,       0x40},   // Right ADC signal path control; R/W
//  
//    {CJC8988_R34_LEFT_OUT_MIX1_H,        0x52},   // Left DAC to left mixer disable; R/W
//    {CJC8988_R35_LEFT_OUT_MIX2_L,        0x50},   // Right DAC to left mixer disable; R/W
//    {CJC8988_R36_RIGHT_OUT_MIX1_L,       0x52},   // Left DAC to right mixer disable; R/W
//    {CJC8988_R37_RIGHT_OUT_MIX2_H,       0x50},   // Right DAC to right mixer disable; R/W
//
//    {CJC8988_R40_LOUT2_VOLUME,           0x79},   // Audio output left channel1 volume; R/W
//    {CJC8988_R41_ROUT2_VOLUME,           0x79},   // Audio output right channel1 volume; R/W
//    {CJC8988_R43_LOW_POWER_PLAYBACK,     0x08},   // Low power playback; R/W
//        
//    {CJC8988_R25_PWR_MGMT1_H,            0x7C},   // Power management1 and VMIDSEL; R/W
//    {CJC8988_R26_PWR_MGMT2_H,            0xf8},   // Power management2 and DAC left power up; R/W;
//};
#define CJC8988_RATES ( \
		SNDRV_PCM_RATE_96000 | \
		SNDRV_PCM_RATE_48000 | \
		SNDRV_PCM_RATE_32000 | \
		SNDRV_PCM_RATE_16000 | \
		SNDRV_PCM_RATE_8000)

// CJC8988 LS2K1000-创龙板
// 深位定死16位，只能播放16位
// 如果给32位，I2S bclk 与 lrc 无法对齐，从信号上看类似于 left-justify 模式
#define CJC8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)

static unsigned char CJC8988_SR_TABLE[][7] = {
	// 8K, 12K, 16K, 24K, 32K, 48K, 96K
	{0x6, 0x8, 0xa, 0x1c, 0xc, 0x0, 0xe}, // 12288000
	{0x7, 0x9, 0xb, 0x1d, 0xd, 0x1, 0xf}, // 18432000
};

/* codec private data */
struct cjc8988_priv {
	struct regmap *regmap;
	unsigned int mode;
	unsigned int freq;
};

static const char * const input_channel_text[] = { "INPUT1", "INPUT2" };

static const struct soc_enum left_input_channel_enum[] = {
	SOC_ENUM_SINGLE(CJC8988_R32_ADCL_SIGNAL_PATH, 6, ARRAY_SIZE(input_channel_text), input_channel_text)
};

static const struct soc_enum right_input_channel_enum[] = {
	SOC_ENUM_SINGLE(CJC8988_R33_ADCR_SIGNAL_PATH, 6, ARRAY_SIZE(input_channel_text), input_channel_text)
};

static const struct snd_kcontrol_new cjc8988_snd_controls[] = {
	SOC_DOUBLE_R_RANGE("Volume", CJC8988_R2_LOUT1_VOLUME, CJC8988_R3_ROUT1_VOLUME,
			0x00, 0x2f, 0x79,0),
	SOC_ENUM("L-Input", left_input_channel_enum),
	SOC_ENUM("R-Input", right_input_channel_enum),
};

static struct reg_default cjc8988_reg_defaults[] = {
	{CJC8988_R0_LEFT_INPUT_VOLUME, 0x17},   // Audio input left channel volume; R/W
	{CJC8988_R1_RIGHT_INPUT_VOLUME, 0x17},   // Audio input right channel volume; R/W
	{CJC8988_R2_LOUT1_VOLUME, 0x79},   // Audio output letf channel1 volume; R/W
	{CJC8988_R3_ROUT1_VOLUME, 0x79},   // Audio output right channel1 volume; R/W
	{CJC8988_R5_ADC_DAC_CONTROL, 0x00},   // ADC and DAC CONTROL; R/W
	{CJC8988_R7_AUDIO_INTERFACE, 0x4E},   // Digital Audio interface format; R/W
	{CJC8988_R8_SAMPLE_RATE, 0x00},   // Clock and Sample rate control; R/W
	{CJC8988_R10_LEFT_DAC_VOLUME, 0xff},   // Left channel DAC digital volume; R/W
	{CJC8988_R11_RIGHT_DAC_VOLUME, 0xff},   // Right channel DAC digital volume; R/W
	{CJC8988_R12_BASS_CONTROL, 0x0f},   // BASS control; R/W
	{CJC8988_R13_TREBLE_CONTROL, 0x0f},   // Treble control; R/W
	{CJC8988_R16_3D_CONTROL, 0x00},   // 3D control; R/W
	{CJC8988_R21_LEFT_ADC_VOLUME, 0xc3},   // Left ADC digital volume; R/W
	{CJC8988_R22_RIGHT_ADC_VOLUME, 0xc3},   // Right ADC digital volume; R/W
	{CJC8988_R23_ADDITIONAL_CONTROL1, 0xc0},   // Additional control 1; R/W
	{CJC8988_R24_ADDITIONAL_CONTROL2, 0x04},   // Additional control 2; R/W
	{CJC8988_R27_ADDITIONAL_CONTROL3, 0x00},   // Additional control 3; R/W
	{CJC8988_R31_ADC_INPUT_MODE, 0x00},   // ADC input mode; R/W
// 0x40 : 默认 INPUT2，0x70 加上 MIC 增益，按需求定
//	{CJC8988_R32_ADCL_SIGNAL_PATH, 0x70},   // Left ADC signal path control; R/W
//	{CJC8988_R33_ADCR_SIGNAL_PATH, 0x70},   // Right ADC signal path control; R/W
	{CJC8988_R32_ADCL_SIGNAL_PATH, 0x00},   // Left ADC signal path control; R/W
	{CJC8988_R33_ADCR_SIGNAL_PATH, 0x00},   // Right ADC signal path control; R/W
	{CJC8988_R34_LEFT_OUT_MIX1_H, 0x52},   // Left DAC to left mixer enable; R/W
	{CJC8988_R35_LEFT_OUT_MIX2_L, 0x50},   // Right DAC to left mixer disable; R/W
	{CJC8988_R36_RIGHT_OUT_MIX1_L, 0x52},   // Left DAC to right mixer disable; R/W
	{CJC8988_R37_RIGHT_OUT_MIX2_H, 0x50},   // Right DAC to right mixer enable; R/W
	{CJC8988_R40_LOUT2_VOLUME, 0x79},   // Audio output left channel1 volume; R/W
	{CJC8988_R41_ROUT2_VOLUME, 0x79},   // Audio output right channel1 volume; R/W
	{CJC8988_R43_LOW_POWER_PLAYBACK, 0x08},   // Low power playback; R/W
	{CJC8988_R25_PWR_MGMT1_H, 0x7c},   // Power management1 and VMIDSEL; R/W
	{CJC8988_R26_PWR_MGMT2_H, 0xf8},   // Power management2 and DAC left power up; R/W;
};

static void cjc8988_default_regs_change(unsigned int reg, unsigned int val)
{
	int i;
	for (i = 0; i < ARRAY_SIZE(cjc8988_reg_defaults); i++) {
		if (reg == cjc8988_reg_defaults[i].reg)
		{
			cjc8988_reg_defaults[i].def = val;
			break;
		}
	}
}

static bool cjc8988_valid_regs(struct device *dev, unsigned int reg)
{
	switch (reg) {
	case CJC8988_R0_LEFT_INPUT_VOLUME:
	case CJC8988_R1_RIGHT_INPUT_VOLUME:
	case CJC8988_R2_LOUT1_VOLUME:
	case CJC8988_R3_ROUT1_VOLUME:
	case CJC8988_R5_ADC_DAC_CONTROL:
	case CJC8988_R7_AUDIO_INTERFACE:
	case CJC8988_R8_SAMPLE_RATE:
	case CJC8988_R10_LEFT_DAC_VOLUME:
	case CJC8988_R11_RIGHT_DAC_VOLUME:
	case CJC8988_R12_BASS_CONTROL:
	case CJC8988_R13_TREBLE_CONTROL:
	case CJC8988_R16_3D_CONTROL:
	case CJC8988_R21_LEFT_ADC_VOLUME:
	case CJC8988_R22_RIGHT_ADC_VOLUME:
	case CJC8988_R23_ADDITIONAL_CONTROL1:
	case CJC8988_R24_ADDITIONAL_CONTROL2:
	case CJC8988_R27_ADDITIONAL_CONTROL3:
	case CJC8988_R31_ADC_INPUT_MODE:
	case CJC8988_R32_ADCL_SIGNAL_PATH:
	case CJC8988_R33_ADCR_SIGNAL_PATH:
	case CJC8988_R34_LEFT_OUT_MIX1_H:
	case CJC8988_R35_LEFT_OUT_MIX2_L:
	case CJC8988_R36_RIGHT_OUT_MIX1_L:
	case CJC8988_R37_RIGHT_OUT_MIX2_H:
	case CJC8988_R40_LOUT2_VOLUME:
	case CJC8988_R41_ROUT2_VOLUME:
	case CJC8988_R43_LOW_POWER_PLAYBACK:
	case CJC8988_R25_PWR_MGMT1_H:
	case CJC8988_R26_PWR_MGMT2_H:
		return true;
	default:
		return false;
	}
}

/*
 * Note that this should be called from init rather than from hw_params.
 */
static int cjc8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
		int clk_id, unsigned int freq, int dir)
{
	return 0;
}

static int cjc8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
		unsigned int fmt)
{
	return 0;
}

static int cjc8988_pcm_startup(struct snd_pcm_substream *substream,
			      struct snd_soc_dai *dai)
{
	return 0;
}

static u8 cjc8988_get_depth(unsigned int bits)
{
	switch (bits)
	{
	case 16:
		return 0x01;
	case 20:
		return 0x05;
	case 24:
		return 0x09;
	case 32:
		return 0x0D;
	default:
		return 0x01;
	}
}

static int cjc8988_sr_table_map_sample_index(unsigned int sample)
{
	switch (sample)
	{
	case 8000:
		return 0;
	case 12000:
		return 1;
	case 16000:
		return 2;
	case 24000:
		return 3;
	case 32000:
		return 4;
	case 48000:
		return 5;
	case 96000:
		return 6;
	default:
		return 0;
	}
}

static u8 cjc8988_get_sample_rate(unsigned int freq, unsigned int sample)
{
	int index = cjc8988_sr_table_map_sample_index(sample);
	switch (freq)
	{
	case 12288000:
		return CJC8988_SR_TABLE[0][index] << 1;
	case 18432000:
		return CJC8988_SR_TABLE[1][index] << 1;
	default:
		return 0;
	}
}

static int cjc8988_pcm_hw_params(struct snd_pcm_substream *substream,
				struct snd_pcm_hw_params *params,
				struct snd_soc_dai *dai)
{
	u8 val;
	struct cjc8988_priv *cjc8988;
	cjc8988 = snd_soc_component_get_drvdata(dai->component);

// NOTE-TMP-REV
// 深位定死32位，目前能播放16位
// 如果codec只给16位，正弦信号半个周期会有尖峰，怀疑与时钟有关
//	val = cjc8988_get_depth(params->msbits);
//	snd_soc_component_write(dai->component, CJC8988_R7_AUDIO_INTERFACE,
//			(val | cjc8988->mode << 6 | 2));

	val = cjc8988_get_sample_rate(cjc8988->freq, params->rate_num);
	snd_soc_component_write(dai->component, CJC8988_R8_SAMPLE_RATE,
			val);

	return 0;
}

static int cjc8988_set_bias_level(struct snd_soc_component *component,
				 enum snd_soc_bias_level level)
{
	return 0;
}

static int cjc8988_component_probe(struct snd_soc_component *component)
{
	int i = 0;
	for (i = 0; i < ARRAY_SIZE(cjc8988_reg_defaults); i++) {
		snd_soc_component_write(component, cjc8988_reg_defaults[i].reg,
				cjc8988_reg_defaults[i].def);
	}
	return 0;
}

static const struct snd_soc_dai_ops cjc8988_dai_ops = {
	.startup = cjc8988_pcm_startup,
	.hw_params = cjc8988_pcm_hw_params,
	.set_fmt = cjc8988_set_dai_fmt,
	.set_sysclk = cjc8988_set_dai_sysclk,
};

static struct snd_soc_dai_driver cjc8988_dai = {
	.name = "cjc8988-hifi",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = CJC8988_RATES,
		.formats = CJC8988_FORMATS,
	},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = CJC8988_RATES,
		.formats = CJC8988_FORMATS,
	 },
	.ops = &cjc8988_dai_ops,
	.symmetric_rates = 1,
};

static const struct snd_soc_component_driver cjc8988_component_driver = {
	.probe			= cjc8988_component_probe,
	.set_bias_level		= cjc8988_set_bias_level,
	.controls		= cjc8988_snd_controls,
	.num_controls		= ARRAY_SIZE(cjc8988_snd_controls),
//	.dapm_widgets		= cjc8988_dapm_widgets,
//	.num_dapm_widgets	= ARRAY_SIZE(cjc8988_dapm_widgets),
//	.dapm_routes		= cjc8988_dapm_routes,
//	.num_dapm_routes	= ARRAY_SIZE(cjc8988_dapm_routes),
	.suspend_bias_off	= 1,
	.idle_bias_on		= 1,
	.use_pmdown_time	= 1,
	.endianness		= 1,
	.non_legacy_dai_naming	= 1,
};

static const struct regmap_config cjc8988_regmap = {
	.reg_bits = 8,
	.val_bits = 8,

	.max_register = CJC8988_REG_NUM,
	.writeable_reg = cjc8988_valid_regs,
	.readable_reg = cjc8988_valid_regs,

	.cache_type = REGCACHE_RBTREE,
	.reg_defaults = cjc8988_reg_defaults,
	.num_reg_defaults = ARRAY_SIZE(cjc8988_reg_defaults),
};

static const struct i2c_device_id cjc8988_id[] = {
	{ "cjc8988", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, cjc8988_id);

static int cjc8988_i2c_probe(struct i2c_client *i2c,
			    const struct i2c_device_id *id)
{
	int ret;
	struct device *dev = &i2c->dev;
	struct cjc8988_priv * cjc8988 = devm_kzalloc(dev, sizeof(*cjc8988), GFP_KERNEL);
	if (cjc8988 == NULL)
		return -ENOMEM;

	cjc8988->regmap = devm_regmap_init_i2c(i2c, &cjc8988_regmap);
	if (IS_ERR(cjc8988->regmap)) {
		ret = PTR_ERR(cjc8988->regmap);
		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
		return ret;
	}

	if (device_property_present(dev, "master-mode")){
		cjc8988->mode = 1;
	}
	else {
		cjc8988->mode = 0;
	}

	device_property_read_u32(&i2c->dev, "clock-frequency", &cjc8988->freq);
	i2c_set_clientdata(i2c, cjc8988);

	if (device_property_present(dev, "cjc8988,adc-input2")){
		cjc8988_default_regs_change(CJC8988_R32_ADCL_SIGNAL_PATH, 0x40);
		cjc8988_default_regs_change(CJC8988_R33_ADCR_SIGNAL_PATH, 0x40);
	}

	return devm_snd_soc_register_component(dev,
			&cjc8988_component_driver, &cjc8988_dai, 1);
}

static struct i2c_driver cjc8988_i2c_driver = {
	.driver = {
		.name		= "cjc8988",
	},
	.probe    = cjc8988_i2c_probe,
	.id_table = cjc8988_id,
};

module_i2c_driver(cjc8988_i2c_driver);

MODULE_DESCRIPTION("ASoC CJC8988 driver");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
